#ifndef _VMA_H
#define _VMA_H

#include <compiler.h>
#include <ds/list.h>
#include <ds/mutex.h>
#include <fs.h>
#include <type.h>
#include <mm/mm.h>
#include <process.h>

struct mm;
struct vma;
struct avm;

struct avm_chain {
    struct vma* vma;
    struct avm* avm;
    lnode_t same_avm;
    lnode_t same_vma;
};

struct avm {
    mutex_t lock;
    struct avm* root;
    atomic_int ref;
    lnode_t avc_list;
};

struct vma {
    struct vma* next, * prev;
    uptr start;
    uptr end;
    u32 flags;
    struct mm* mm;
    struct avm* avm;
    lnode_t avm_chain;
    size_t file_offset;
    file_t* file;
};

#define VMA_PRIVATE     (1 << 31)
#define VMA_READ_SHARE  (1 << 1)
#define VMA_WRITE_SHARE (1 << 2)
#define VMA_READ        (1 << 3)
#define VMA_WRITE       (1 << 4)
#define VMA_NO_SWAP     (1 << 5)

static inline void avm_lock(struct avm* avm) {
    mutex_lock(&avm->lock);
}

static inline void avm_unlock(struct avm* avm) {
    mutex_unlock(&avm->lock);
}

static inline u32 vma_writeable(struct vma* vma) {
    return vma->flags & (VMA_WRITE | VMA_WRITE_SHARE);
}

static inline struct vma* query_vma(struct mm* mm, uptr vaddr) {
    struct vma* vma;
    for (vma = mm->vma_list; vma; vma = vma->next) {
        if (vma->start <= vaddr && vaddr < vma->end) {
            return vma;
        }
    }
    return NULL;
}

void link_avm_page(struct avm* avm, page_t* page, uptr vaddr);

void vma_init();

struct vma* vma_alloc();

struct avm_chain* avm_chain_alloc();

struct avm* avm_alloc();

void avm_chain_free(struct avm_chain* avc);

void avm_free(struct avm* avm);

void unlink_avms(struct vma* vma);

int avm_clone(struct vma* dst, struct vma* src);

int avm_fork(struct vma* src, struct vma* dst);

void vma_free(struct vma* vma);

void vma_list_append(struct mm* mm, struct vma* new);

#endif /* _VMA_H */